package com.googlecode.animator.view.properties
{
	import com.googlecode.animator.data.Notifications;
	import com.googlecode.animator.data.materials.BitmapFileMaterialVO;
	import com.googlecode.animator.data.materials.ColorMaterialVO;
	import com.googlecode.animator.data.materials.MaterialVO;
	import com.googlecode.animator.data.materials.MovieMaterialVO;
	import com.googlecode.animator.data.materials.VideoStreamMaterialVO;
	import com.googlecode.animator.data.objects3d.CameraProxyVO;
	import com.googlecode.animator.data.objects3d.CubeVO;
	import com.googlecode.animator.data.objects3d.Object3DVO;
	import com.googlecode.animator.data.objects3d.PlaneVO;
	import com.googlecode.animator.data.objects3d.SphereVO;
	import com.googlecode.animator.data.objects3d.TargetProxyVO;
	import com.googlecode.animator.model.ProjectSettingsProxy;
	
	import flash.events.Event;
	import flash.events.MouseEvent;
	import flash.filesystem.File;
	import flash.net.FileFilter;
	import flash.utils.Dictionary;
	
	import mx.controls.ColorPicker;
	import mx.controls.sliderClasses.Slider;
	import mx.events.CloseEvent;
	import mx.events.ColorPickerEvent;
	import mx.events.SliderEvent;
	import mx.managers.PopUpManager;
	
	import org.puremvc.as3.interfaces.INotification;
	import org.puremvc.as3.patterns.mediator.Mediator;

	/**
	 * Controls the PropertiesPopUp, which contains controls for the materials of an object3D 
	 * @author USER
	 * 
	 */
	public class PropertiesPopupMediator extends Mediator
	{
		/**
		 * Identifier. 
		 */
		public static const NAME:String = "PropertiesPopupMediator";
		
		/**
		 * Stores a reference to the name of the object3D currently selected. 
		 */
		private var _name:String;
		
		/**
		 * Used to hold a local version of the material list. Will probably be deprecated.
		 * The PropertiePopup should be updated through MVC. 
		 */	
		private var _materialsList:Dictionary;

		/**
		 * File. 
		 */
		protected var _file:File;
		
		/**
		 * Constructor. 
		 * @param viewComponent
		 * 
		 */
		public function PropertiesPopupMediator(viewComponent:Object=null)
		{
			super(NAME, viewComponent);
		}
		
		///////////////////////////////////////////////
		//
		// PureMVC.
		//
		///////////////////////////////////////////////
		
		/**
		 * Integrates component with MVC framework. 
		 * 
		 */
		override public function onRegister():void
		{
			super.onRegister();
			
			//store reference to the currently selected object3D
			_name = settings.selectedObject3D;
			
			// add event listeners to the controls
			viewRef.fillColor.addEventListener(ColorPickerEvent.CHANGE, onFillColor);
			viewRef.fillAlpha.addEventListener(SliderEvent.THUMB_RELEASE, onFillAlpha);
			viewRef.browse.addEventListener(MouseEvent.CLICK, onBrowse);
			viewRef.addEventListener(CloseEvent.CLOSE, deletePopup);
			
			// ensure unfolded square, used only for cubes, is not visible by default.
			viewRef.unfoldedSquareVisible(false);
			
			// ensure that if no object3D is selected, the controls are hidden.
			var name:String = settings.selectedObject3D;
			
			if(name == null)
			{
				controlsVisible(false);
				
			}else
			{
				controlsVisible(true);
				sortMaterial(settings.getObject3D(name));
			} 
			
				
		}
		
		/**
		 * Called when the mediator is removed by the MVC framework. 
		 * 
		 */
		override public function onRemove():void
		{
			super.onRemove();
			
			viewRef.fillColor.removeEventListener(ColorPickerEvent.CHANGE, onFillColor);
			viewRef.fillAlpha.removeEventListener(SliderEvent.THUMB_RELEASE, onFillAlpha);
			viewRef.browse.removeEventListener(MouseEvent.CLICK, onBrowse);
			viewRef.unfoldedSquare.removeEventListener(MouseEvent.CLICK, onFaceSelected);
			viewRef.removeEventListener(CloseEvent.CLOSE, deletePopup);
		}
		
		/**
		 * List notification of interests. 
		 * @return 
		 * 
		 */
		override public function listNotificationInterests():Array
		{
			var list:Array = super.listNotificationInterests();
			
			list.push(Notifications.OBJECT3D_SELECTED);
			list.push(Notifications.OBJECT3D_DELETED);
			list.push(Notifications.OBJECT3D_MATERIAL_CHANGED);
			
			return list;
		}
		
		/**
		 * Responds to notified interests. 
		 * @param notification
		 * 
		 */
		override public function handleNotification(notification:INotification):void
		{
			super.handleNotification(notification);

			switch(notification.getName())
			{
				case Notifications.OBJECT3D_SELECTED:
				
					handleObject3DSelected(notification);

					break;
				
				case Notifications.OBJECT3D_DELETED:
				
					handleObject3DDeleted();
					
					break;
					
				case Notifications.OBJECT3D_MATERIAL_CHANGED:

					handleMaterialChanged(notification);
					
					break;
			}
		}
		
		///////////////////////////////////////////////
		//
		// PureMVC handlers.
		//
		///////////////////////////////////////////////
		
		/**
		 * Called when a user selects an object3D. 
		 * @param notification
		 * 
		 */
		protected function handleObject3DSelected(notification:INotification):void
		{	
			// checked ok
			// ensure unfolded square, used only for cubes, is not visible by default.
			viewRef.unfoldedSquareVisible(false);
			
			_name = notification.getBody() as String;
			var object3D:Object3DVO = settings.getObject3D(_name);

			// if the user has selected a camera or its target, hide the controls
			if(object3D is CameraProxyVO || object3D is TargetProxyVO)
			{
				controlsVisible(false);
			}else
			{
				controlsVisible(true);
			}
			
			sortMaterial(object3D);
		}
		
		/**
		 * Called when an object3D is deleted. 
		 * 
		 */
		protected function handleObject3DDeleted():void
		{
			deletePopup();		
		}
		
		/**
		 * Called when the material of an object3D has been changed. 
		 * @param notification
		 * 
		 */
		protected function handleMaterialChanged(notification:INotification):void
		{
			// checked ok
			if(notification.getBody() is Object3DVO)
			{				
				sortMaterial(notification.getBody() as Object3DVO);
			}
			
		}
		
		///////////////////////////////////////////////
		//
		// Sorting out what to do.
		//
		///////////////////////////////////////////////
		
		/**
		 * The type of object3D affects how it will be handled in terms
		 * of allocating materials to faces. This functions carries out a basic sort. 
		 * @param object3D
		 * 
		 */
		protected function sortMaterial(object3D:Object3DVO):void
		{
			// make sure we have removed event listener on unfolded square.
			viewRef.unfoldedSquare.removeEventListener(MouseEvent.CLICK, onFaceSelected);
			
			if(object3D is PlaneVO)
			{
				handleOneFacedObject(object3D as PlaneVO);
			}
			
			if(object3D is CubeVO)
			{
				handleCube(object3D as CubeVO);
			}
			
			if(object3D is SphereVO)
			{
				handleOneFacedObject(object3D as SphereVO);
			}
		}
		
		/**
		 * If the object3D is a Cube, an extra control is needed so that the user can select a face. 
		 * @param object3D
		 * 
		 */
		protected function handleCube(object3D:CubeVO):void
		{	
			// checked ok
			viewRef.unfoldedSquareVisible(true);
			viewRef.unfoldedSquare.addEventListener(MouseEvent.CLICK, onFaceSelected);

			object3D.selectedFace = object3D.selectedFace != null ? object3D.selectedFace : CubeVO.BACK_FACE;

			viewRef.unfoldedSquare.faceName = object3D.selectedFace;
			
			setFaceNameText("Face: " + object3D.selectedFace);
			setColorMaterialControls(object3D, object3D.selectedFace);
			setupMediaSquare(object3D, object3D.selectedFace);
			setRadioButtons(object3D, object3D.selectedFace);		
			
		}
		
		/**
		 * Called if the object3D has only one face. 
		 * @param object3D
		 * 
		 */
		protected function handleOneFacedObject(object3D:Object3DVO):void
		{	
			viewRef.unfoldedSquare.faceName = PlaneVO.FRONT_FACE;
			
			setFaceNameText("Face: " + PlaneVO.FRONT_FACE);	
			setColorMaterialControls(object3D);
			setupMediaSquare(object3D); 
			setRadioButtons(object3D);
		}
		
		///////////////////////////////////////////////
		//
		// Event handlers.
		//
		///////////////////////////////////////////////
		
		/**
		 * In the case of objects with more than one face, the Cube for example,
		 * the user must select a face to which a new material can be applied.
		 * @param evt
		 * 
		 */
		protected function onFaceSelected(evt:MouseEvent=null):void
		{
			_name = settings.selectedObject3D;
			
			var object3D:Object3DVO = settings.getObject3D(_name);
			var faceName:String = viewRef.unfoldedSquare.faceName != null ? viewRef.unfoldedSquare.faceName : CubeVO.BACK_FACE;

			if(object3D is CubeVO)
			{
				(object3D as CubeVO).selectedFace = faceName;

			}
			
			setFaceNameText("Face: " + faceName);
			setColorMaterialControls(object3D, faceName);
			setupMediaSquare(object3D, faceName);
			setRadioButtons(object3D, faceName);
	
		}
		
		/**
		 * Called when a user selects a new colour for a face of an object3D.
		 * @param evt
		 * 
		 */
		protected function onFillColor(evt:ColorPickerEvent):void
		{
			// when a fill colour is selected, a ColorMaterial needs to be assigned to the selected face
			// for the assignment to work, we need to know the face name, the colour value and the object3D name.
			
			var object3D:Object3DVO = settings.getObject3D(_name);
			
			var mat:ColorMaterialVO = new ColorMaterialVO();
			mat.face = viewRef.unfoldedSquare.faceName;
			
			if(object3D is CubeVO)
			{
				(object3D as CubeVO).selectedFace = viewRef.unfoldedSquare.faceName;
				
			}  
			
			if(mat is ColorMaterialVO)
			{
				(mat as ColorMaterialVO).fillColor = evt.color;
				(mat as ColorMaterialVO).fillAlpha = (viewRef.fillAlpha as Slider).value / 100;
			}
			

			sendNotification(Notifications.UPDATE_OBJECT3D_MATERIAL, addMaterialToObject3D(object3D, mat));
		}
		
		/**
		 * Called when a user selects a new fillAlpha for a face of an object3D. 
		 * @param evt
		 * 
		 */
		protected function onFillAlpha(evt:SliderEvent):void
		{
			var object3D:Object3DVO = settings.getObject3D(_name);
			
			var mat:ColorMaterialVO = new ColorMaterialVO();
			mat.face = viewRef.unfoldedSquare.faceName;
			
			if(object3D is CubeVO)
			{
				(object3D as CubeVO).selectedFace = viewRef.unfoldedSquare.faceName;
				
			}
			
			if(mat is ColorMaterialVO)
			{
				(mat as ColorMaterialVO).fillColor = (viewRef.fillColor as ColorPicker).selectedColor;
				(mat as ColorMaterialVO).fillAlpha = evt.value  / 100;
			}
			
			sendNotification(Notifications.UPDATE_OBJECT3D_MATERIAL, addMaterialToObject3D(object3D, mat));
		}
		
		/**
		 * Called when a user opens a file browser to search for a new material for a face of an object3D. 
		 * @param evt
		 * 
		 */
		protected function onBrowse(evt:MouseEvent):void
		{
			var ff:FileFilter = new FileFilter("SWF, FLV, Image", "*.swf; *.flv; *.gif; *.jpg; *.jpeg; *.png;");
			
			if(_file == null)
			{
				_file = File.documentsDirectory;
			}
				
			_file.addEventListener(Event.SELECT, onFileSelect);
			_file.browseForOpen("Open",[ff]);
		}
		
		/**
		 * Called when a user selects a material for a face of an object3D. 
		 * @param evt
		 * 
		 */
		protected function onFileSelect(evt:Event):void
		{	
			_file.removeEventListener(Event.SELECT, onFileSelect);
			
			var length:uint = ((evt.target as File).nativePath as String).length;
			var fp:String = (evt.target as File).url;
			
			var object3D:Object3DVO = settings.getObject3D(_name);
			var mat:MaterialVO;
			
			if(object3D is CubeVO)
			{
				(object3D as CubeVO).selectedFace = viewRef.unfoldedSquare.faceName;
				
			}
		
			if(fp.search(".jpg") != -1 || fp.search(".jpeg") != -1 || fp.search(".gif") != -1 || fp.search(".png") != -1)
			{
				mat = new BitmapFileMaterialVO();
				(mat as BitmapFileMaterialVO).filePath = fp;
				
			}else if(fp.search(".swf") != -1)
			{
				mat = new MovieMaterialVO();
				(mat as MovieMaterialVO).filePath = fp;

			}else if(fp.search(".flv") != -1)
			{
				mat = new VideoStreamMaterialVO();
				(mat as VideoStreamMaterialVO).filePath = fp;
				
			}else
			{
				return;
			}
			
			mat.face = viewRef.unfoldedSquare.faceName;

			sendNotification(Notifications.UPDATE_OBJECT3D_MATERIAL, addMaterialToObject3D(object3D, mat));
		}
		
		///////////////////////////////////////////////
		//
		// Set up pop-up.
		//
		///////////////////////////////////////////////
		
		protected function setupMediaSquare(object3D:Object3DVO, faceName:String = CubeVO.BACK_FACE):void
		{
			//var object3D:Object3DVO = settings.getObject3D(_name);
			var mat:MaterialVO = getTypedMaterial(object3D, faceName);

			if(mat is ColorMaterialVO)
			{
				viewRef.face.setMedia(PropertiesPopupMediaSquare.COLOUR, (mat as ColorMaterialVO).fillColor, (mat as ColorMaterialVO).fillAlpha);
			}
			
			if(mat is BitmapFileMaterialVO)
			{
				viewRef.face.setMedia(PropertiesPopupMediaSquare.IMAGE, 0, 0, (mat as BitmapFileMaterialVO).filePath);
			}
			
			if(mat is MovieMaterialVO)
			{
				viewRef.face.setMedia(PropertiesPopupMediaSquare.IMAGE, 0, 0, (mat as MovieMaterialVO).filePath);
			}
			
			if(mat is VideoStreamMaterialVO)
			{
				viewRef.face.setMedia(PropertiesPopupMediaSquare.VIDEO, 0, 0, (mat as VideoStreamMaterialVO).filePath);
			}
			
			
		}
		
		protected function setColorMaterialControls(object3D:Object3DVO, faceName:String = CubeVO.BACK_FACE):void
		{
			
			var mat:MaterialVO = getTypedMaterial(object3D, faceName);
			
			viewRef.fillColor.selectedColor = ColorMaterialVO.FILL_COLOR;
			viewRef.fillAlpha.setThumbValueAt(0, ColorMaterialVO.FILL_ALPHA);
			
			if(mat is ColorMaterialVO)
			{
				viewRef.fillColor.selectedColor = (mat as ColorMaterialVO).fillColor;
				viewRef.fillAlpha.setThumbValueAt(0, (mat as ColorMaterialVO).fillAlpha * 100);
			}
			
		}
		
		protected function setFaceNameText(text:String):void
		{
			viewRef.faceText = text;
		}
		
		protected function setRadioButtons(object3D:Object3DVO, faceName:String = PlaneVO.FRONT_FACE):void
		{
			var mat:MaterialVO = getTypedMaterial(object3D, faceName);
			
			if(mat is ColorMaterialVO)
			{
				viewRef.colorMaterial.selected = true;
			}else
			{
				viewRef.media.selected = true;
			}
		}
		
		/**
		 * Deletes popup and carries out any clean-up operations. 
		 * @param evt
		 * 
		 */
		protected function deletePopup(evt:CloseEvent = null):void
		{
			facade.removeMediator(PropertiesPopupMediator.NAME);
			PopUpManager.removePopUp(viewRef);
		}
		
		
		
		
		/**
		 * Sets the visibility of the controls. 
		 * @param value
		 * 
		 */
		protected function controlsVisible(value:Boolean):void
		{
			viewRef.fillColor.visible = value;
			viewRef.fillAlpha.visible = value;
			viewRef.browse.visible = value;
		}
		
		///////////////////////////////////////////////
		//
		// File handlers.
		//
		///////////////////////////////////////////////
					
		
		///////////////////////////////////////////////
		//
		// Utils.
		//
		///////////////////////////////////////////////
		
		protected function addMaterialToObject3D(object3D:Object3DVO, mat:MaterialVO):Object3DVO
		{
			if(object3D is PlaneVO)
			{
				(object3D as PlaneVO).material = mat;
			}
			
			if(object3D is CubeVO)
			{
				(object3D as CubeVO).materialsList[(object3D as CubeVO).selectedFace] = mat;
			}
			
			if(object3D is SphereVO)
			{
				(object3D as SphereVO).material = mat;
			}
			
			return object3D;
		}
		
		protected function getTypedMaterial(object3D:Object3DVO, faceName:String = CubeVO.BACK_FACE):MaterialVO
		{
			var mat:MaterialVO;
			
			if(object3D is PlaneVO)
			{
				mat = (object3D as PlaneVO).material as MaterialVO;
			}
			
			if(object3D is CubeVO)
			{
				mat = (object3D as CubeVO).materialsList[faceName] as MaterialVO;
			}
			
			if(object3D is SphereVO)
			{
				mat = (object3D as SphereVO).material as MaterialVO;
			}
			
			return mat;
		}
		
		/**
		 * Returns a typed reference to the model. 
		 * @return 
		 * 
		 */
		protected function get settings():ProjectSettingsProxy
		{		
			return facade.retrieveProxy(ProjectSettingsProxy.NAME) as ProjectSettingsProxy;
		}
		
		
		/**
		 * Returns typed reference to component. 
		 * @return 
		 * 
		 */
		protected function get viewRef():PropertiesPopup
		{
			return viewComponent as PropertiesPopup;
		}
	}
}